﻿using System;
using System.Data.SqlClient;
using System.Data;
using System.Data.Common;

namespace Flagwind.Data.Providers
{
	/// <summary>
	/// SqlServer 数据访问提供程序类。
	/// </summary>
	public sealed class SqlServer : DataProviderBase
	{
		#region 公共属性

		/// <summary>
		/// 当前数据库的类型枚举。
		/// </summary>
		public override DataProviderType ProviderType
		{
			get
			{
				return DataProviderType.SqlServer;
			}
		}

		/// <summary>
		/// 获取连接字符串构造类。
		/// </summary>
		public override DbConnectionStringBuilder ConnectionStringBuilder
		{
			get
			{
				return new SqlConnectionStringBuilder(this.ConnectionString);
			}
		}

		/// <summary>
		/// 获取连接数据库用户的Id。
		/// </summary>
		public override string ConnectionUserId
		{
			get
			{
				return ((SqlConnectionStringBuilder)ConnectionStringBuilder).UserID;
			}
		}

		#endregion

		#region 获取方法

		/// <summary>
		/// 获取数据库连接对象实例。
		/// </summary>
		/// <param name="connectionString">连接字符串。</param>
		/// <returns>数据库连接对象</returns>
		public override IDbConnection GetConnection(string connectionString)
		{
			this.ConnectionString = connectionString;
			return new SqlConnection(this.ConnectionString);
		}

		/// <summary>
		/// 获取一个新参数对象。
		/// </summary>
		/// <returns>特定于数据源的参数对象。</returns>
		public override IDataParameter GetParameter()
		{
			return new SqlParameter();
		}

		/// <summary>
		/// 获取一个新参数对象。
		/// </summary>
		/// <param name="name">参数名字。</param>
		/// <param name="type">数据库数据类型。</param>
		/// <param name="size">字段大小。</param>
		/// <returns>SQL Server 参数对象。</returns>
		public override IDataParameter GetParameter(string name, DbType type, int size)
		{
			var parameter = new SqlParameter();
			parameter.ParameterName = name;
			parameter.DbType = type;
			parameter.Size = size;
			return parameter;
		}

		/// <summary>
		/// 返回此 SqlConnection 的数据源的架构信息。
		/// </summary>
		/// <param name="collectionName">集合名称，可以为空</param>
		/// <param name="restrictionValues">请求的架构的一组限制值，可以为空</param>
		/// <returns>数据库架构信息表</returns>
		public override DataTable GetSchema(string collectionName, string[] restrictionValues)
		{
			using(var connection = (SqlConnection)this.GetConnection())
			{
				connection.Open();
				if(restrictionValues == null && string.IsNullOrEmpty(collectionName))
				{
					return connection.GetSchema();
				}
				else if(restrictionValues == null && !string.IsNullOrEmpty(collectionName))
				{
					return connection.GetSchema(collectionName);
				}
				else
				{
					return connection.GetSchema(collectionName, restrictionValues);
				}
			}
		}

		/// <summary>
		/// 获取数据适配器实例。
		/// </summary>
		/// <returns>数据适配器。</returns>
		public override IDbDataAdapter GetDataAdapter(IDbCommand command)
		{
			return new SqlDataAdapter((SqlCommand)command);
		}

		/// <summary>
		/// 获取当前数据库类型的参数数据类型名称。
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		public override string GetNativeDbTypeName(IDataParameter parameter)
		{
			return ((SqlParameter)parameter).SqlDbType.ToString();
		}

		/// <summary>
		/// 获取存储过程、函数的定义内容。
		/// </summary>
		/// <param name="name">存储过程名称。</param>
		/// <returns>定义内容。</returns>
		public override string GetProcDetail(string name)
		{
			string value = string.Empty;
			var dataSet = this.ExecuteDataSet("sp_helptext", CommandType.StoredProcedure, new IDataParameter[]
			{
				this.GetParameter("@objname", name)
			});
			if(dataSet != null && dataSet.Tables.Count > 0)
			{
				var table = dataSet.Tables[0];
				for(int i = 0; i < table.Rows.Count; i++)
				{
					value += table.Rows[i][0].ToString();
				}
			}
			else
			{
				value = "nothing";
			}
			return value;
		}

		/// <summary>
		/// 获取视图定义，如果子类支持，需要在子类中重写
		/// </summary>
		/// <param name="name">视图名称</param>
		/// <returns></returns>
		public override string GetViewDetail(string name)
		{
			return GetProcDetail(name);
		}

		#endregion

		#region 执行方法

		/// <summary>
		/// 执行查询,并以指定的(具有数据架构的)数据集来填充数据。
		/// </summary>
		/// <param name="sql">查询语句。</param>
		/// <param name="commandType">命令类型。</param>
		/// <param name="parameters">查询参数。</param>
		/// <param name="schemaDataSet">指定的(具有数据架构的)数据集。</param>
		/// <returns>具有数据的数据集。</returns>
		public override DataSet ExecuteDataSetWithSchema(string sql, CommandType commandType, IDataParameter[] parameters, DataSet schemaDataSet)
		{
			var connection = GetConnection();
			var command = connection.CreateCommand();
			this.ImproveCommand(command, ref sql, ref commandType, ref parameters);
			var adapter = new SqlDataAdapter((SqlCommand)command);
			var logger = new Logger(true);
			try
			{
				if(schemaDataSet.Tables.Count > 0)
				{
					adapter.Fill(schemaDataSet, schemaDataSet.Tables[0].TableName);
				}
				else
				{
					adapter.Fill(schemaDataSet);
				}
			}
			catch(Exception ex)
			{
				this.ErrorMessage = ex.Message;
				bool inTransaction = command.Transaction != null;
				logger.Error(command, "Flagwind.Data:" + ErrorMessage);
				if(OnErrorThrow)
				{
					throw new QueryException(ex.Message, command.CommandText, commandType, parameters, inTransaction, connection.ConnectionString);
				}
			}
			finally
			{
				long lastElapsedTime;
				logger.Log(command, "Flagwind.Data", out lastElapsedTime);
				this.LastElapsedTime = lastElapsedTime;
				this.CloseConnection(connection, command);
			}
			return schemaDataSet;
		}

		/// <summary>
		/// 执行插入数据的查询。
		/// </summary>
		/// <param name="sql">插入数据的SQL。</param>
		/// <param name="commandType">命令类型。</param>
		/// <param name="parameters">参数数组。</param>
		/// <param name="id">要传出的本次操作的新插入数据行的自增主键ID值，如果没有获取到，则为-1。</param>
		/// <returns>本次查询受影响的行数。</returns>
		public override int ExecuteInsertQuery(string sql, CommandType commandType, IDataParameter[] parameters, ref object id)
		{
			var connection = this.GetConnection();
			var command = connection.CreateCommand();
			var logger = new Logger(true);
			this.ImproveCommand(command, ref sql, ref commandType, ref parameters);
			bool inner = false;
			int result = -1;
			id = 0;
			try
			{
				if(command.Transaction == null)
				{
					inner = true;
					command.Transaction = connection.BeginTransaction();
				}
				command.CommandText = sql + " ;SELECT SCOPE_IDENTITY();";
				id = command.ExecuteScalar();
				//如果在内部开启了事务则提交事务，否则外部调用者决定何时提交事务
				result = 1;
				if(inner)
				{
					command.Transaction.Commit();
					command.Transaction = null;
				}
			}
			catch(Exception ex)
			{
				ErrorMessage = ex.Message;
				bool inTransaction = command.Transaction != null;
				if(command.Transaction != null)
				{
					command.Transaction.Rollback();
				}
				if(inner)
				{
					command.Transaction = null;
				}
				logger.Error(command, "Flagwind.Data:" + ErrorMessage);
				if(OnErrorThrow)
				{
					throw new QueryException(ex.Message, command.CommandText, commandType, parameters, inTransaction, connection.ConnectionString);
				}
			}
			finally
			{
				if(command.Transaction == null && connection.State == ConnectionState.Open)
				{
					connection.Close();
				}
			}
			long _elapsedMilliseconds;
			logger.Log(command, "Flagwind.Data", out _elapsedMilliseconds);
			base.LastElapsedTime = _elapsedMilliseconds;
			return result;
		}

		#endregion
	}
}